using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Drawing.Text;
using System.Windows.Forms;
using WbsEditor.Extensions.Disposables;
using WbsEditor.Extensions.Matrices;
using WbsEditor.Extensions.Points;
using WbsEditor.Generation;

namespace WbsEditor
{
    public sealed partial class WbsCanvas 
       : ScrollableZoomableControl, IWbsNodeFactory
    {
        #region Constructor and Initialization
        
        public WbsCanvas()
        {
            InitializeComponent();

            _wbs = new WorkBreakdownStructure();
            _settings = new WbsSettings();
            _lastEstimationPercentage = 50;
            _readOnly = false;

            this.Padding = new Padding(50);
            this.Center = true;

            WbsNodeSelectionChanged += UpdateContextMenuItems;

            this.LayoutTreeAndRefresh();
        }

        #endregion

        #region Properties
        private WorkBreakdownStructure _wbs;
        private WbsNode _selectedNode;
        private WbsSettings _settings;
        private bool _quickEditMode;
        private bool _propertiesEditMode;
        private bool _batchEditMode;
        private bool AnyEditMode { get { return _quickEditMode || _propertiesEditMode || _batchEditMode; } }
        private WbsTfsClient _tfsClient;
        private int _lastEstimationPercentage;
        private bool _readOnly;

        

        [Browsable(false)]
        public WbsTfsClient TfsClient
        {
            get
            {
                if (null == _tfsClient)
                {
                    TfsClient = new WbsTfsClient();
                }
                return _tfsClient;
            }
            set
            {
                if (null != _tfsClient)
                    _tfsClient.SuccessfullyConnected -= TfsClient_OnSuccessfullyConnected;
                _tfsClient = value;
                if (null != _tfsClient)
                    _tfsClient.SuccessfullyConnected += TfsClient_OnSuccessfullyConnected;
            }
        }

        [Browsable(false)]
        public WorkBreakdownStructure WorkBreakdownStructure
        {
            get { return _wbs; }
            private set
            {
                if (null != _wbs)
                {
                    _wbs.TotalWorkChanged -= onWorkBreakdownStructure_TotalWorkChanged;
                }
                _wbs = value;
                _wbs.TotalWorkChanged += onWorkBreakdownStructure_TotalWorkChanged;

                _undoStack.Clear();
                _redoStack.Clear();

                this.Remeasure();
            }
        }

        [Browsable(false)]
        public WbsNode Root
        {
            get
            {
                if (null == _wbs)
                    return null;
                return _wbs.Root;
            }
        }

        [Browsable(false)]
        public WbsNode SelectedNode
        {
            get { return _selectedNode; }
            set
            {
                bool sendEvent = (_selectedNode != value);
                if (null != _selectedNode)
                    _selectedNode.IsSelected = false;
                _selectedNode = value;
                if (null != _selectedNode)
                    _selectedNode.IsSelected = true;
                if (sendEvent && null != WbsNodeSelectionChanged)
                    WbsNodeSelectionChanged(this, new WbsEventArgs(_selectedNode));
                if (null != _selectedNode)
                {
                    if (_selectedNode.IsLinkToWbs)
                        openWBSToolStripMenuItem.Visible = true;
                    else
                        openWBSToolStripMenuItem.Visible = false;
                }
                else
                {
                    openWBSToolStripMenuItem.Visible = false;
                }
            }
        }

        [Browsable(true)]
        public WbsSettings Settings
        {
            get { return _settings; }
            set { _settings = value; }
        }

        [Browsable(false)]
        private WbsNodeEditControl QuickEdit
        {
            get { return _quickEditPanel; }
        }

        [Browsable(false)]
        private BatchChildEditControl BatchEdit
        {
            get { return _batchEditPanel; }
        }

        [Browsable(false)]
        private WbsPropertiesEditControl PropertiesPanel
        {
            get { return _propertiesPanel; }
        }

        [Browsable(false)]
        public int EstimationPercentageDefault
        {
            get { return _lastEstimationPercentage; }
            set { _lastEstimationPercentage = value; }
        }

        [Browsable(false)]
        public bool ReadOnly
        {
            get { return _readOnly; }
            set { _readOnly = value; }
        }

        [Browsable(true)]
        public string CurrentWorkItemType
        {
            get;
            set;
        }

        public string[] GetAssignees()
        {
            List<string> names = new List<string>();
            FindAssignees(Root, names);
            names.Sort();
            return names.ToArray();
        }
        public event EventHandler AssigneeListChanged;
        private void FindAssignees(WbsNode node, List<string> names)
        {
            if (!names.Contains(node.AssignedTo))
                names.Add(node.AssignedTo);
            foreach (WbsNode child in node.ChildNodes)
                FindAssignees(child, names);
        }

        public event TfsConnectEventHandler TfsConnectRequested;

        public event EventHandler TfsSuccessfullyConnected;

        public event EventHandler<WbsEventArgs> WbsNodeSelectionChanged;

        public event WbsLengthyOperationEventHandler LengthyOperationStarted;

        public event WbsLengthyOperationEventHandler LengthyOperationTick;

        public event WbsLengthyOperationEventHandler LengthyOperationFinished;

        public event EventHandler WbsRootWasPublished;

        public event EventHandler LayoutChanged;

        public event EventHandler DirtyFlagSet;

        public event EventHandler DirtyFlagReset;

        public event EventHandler RequestFindUI;

        public event EventHandler EditModeEntered;

        public event EventHandler EditModeExited;

        private void FireRequestFindUI()
        {
            if (null != RequestFindUI)
            {
                RequestFindUI(this, new EventArgs());
            }
        }

        private void FireEditModeEntered()
        {
            EventHandler handler = this.EditModeEntered;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        private void FireEditModeExited()
        {
            EventHandler handler = this.EditModeExited;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        private void SetDirty()
        {
            if (ReadOnly)
                return;

            bool reallySetDirty = true;
            //if (!WorkBreakdownStructure.IsDirty)
            {
                if (!HasChangeInXml())
                {
                    // Was Clean and no change was performed.
                    reallySetDirty = false;
                }
            }
            if (reallySetDirty)
            {
                WorkBreakdownStructure.IsDirty = true;
                if (null != DirtyFlagSet)
                    DirtyFlagSet(this, new EventArgs());
            }
            else
            {
                SetClean();
            }
        }

        private void SetClean()
        {
            WorkBreakdownStructure.IsDirty = false;
            if (null != DirtyFlagReset)
                DirtyFlagReset(this, new EventArgs());
        }
        #endregion

        #region Undo Redo
        Stack<WorkBreakdownStructure> _undoStack = new Stack<WorkBreakdownStructure>();
        Stack<WorkBreakdownStructure> _redoStack = new Stack<WorkBreakdownStructure>();
        #endregion

        #region Overridden Event Handlers

        protected override void OnPaintContent(Graphics g)
        {
            if (null == Root)
                return;

            WbsRenderer.RenderWbs(Root, Settings, g, WorkBreakdownStructure, null, TfsClient.Iteration);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (CheckEditModes())
            {
                return;
            }
            base.OnMouseDown(e);

            Point p = this.ClientPointToContent(e.Location);

            if (WbsLayoutEngine.CheckCollapseAction(Root, p))
            {
                SetDirty();
                LayoutTreeAndRefresh();
                return;
            }
            WbsNode node = WbsLayoutEngine.FindNode(Root, p);

            if (SelectedNode != node)
            {
                SelectedNode = node;
                EnsureSelectedNodeIsVisibleAndRefresh();
            }

            if (e.Button == MouseButtons.Right)
            {
                if (node != null)
                {
                    if (node.LayoutMode == WbsNode.NodeLayout.Horizontal)
                    {
                        horizontalToolStripMenuItem.Checked = true;
                        verticalToolStripMenuItem.Checked = false;
                    }
                    else
                    {
                        horizontalToolStripMenuItem.Checked = false;
                        verticalToolStripMenuItem.Checked = true;
                    }
                    _nodeContextMenu.Show(this, e.Location);
                }
                else
                {
                    if (Settings.Layout == WbsSettings.LayoutSize.Normal)
                    {
                        _normalViewToolStripMenuItem.Checked = true;
                        _smallViewToolStripMenuItem.Checked = false;
                        _largeViewToolStripMenuItem.Checked = false;
                    }
                    else if (Settings.Layout == WbsSettings.LayoutSize.Small)
                    {
                        _normalViewToolStripMenuItem.Checked = false;
                        _smallViewToolStripMenuItem.Checked = true;
                        _largeViewToolStripMenuItem.Checked = false;
                    }
                    else
                    {
                        _normalViewToolStripMenuItem.Checked = false;
                        _smallViewToolStripMenuItem.Checked = false;
                        _largeViewToolStripMenuItem.Checked = true;
                    }
                    _canvasContextMenu.Show(this, e.Location);
                }
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == MouseButtons.Left)
            {
                if (SelectedNode != null)
                {
                    DoDragDrop(SelectedNode, DragDropEffects.Move | DragDropEffects.Copy);
                }
            }
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            if (CheckEditModes())
            {
                return;
            }
            base.OnDoubleClick(e);

            if (SelectedNode == null)
            {
                return;
            }

            EnterQuickEditMode();
        }

        protected override void OnDragOver(DragEventArgs drgevent)
        {
            base.OnDragOver(drgevent);

            Point point = this.ClientPointToContent(PointToClient(new Point(drgevent.X, drgevent.Y)));

            WbsNode node = WbsLayoutEngine.FindNode(Root, point);
            if (null != node &&
                node != SelectedNode &&
                ((drgevent.KeyState & 8) == 8))
            {
                drgevent.Effect = DragDropEffects.Copy;
            }
            else if (null != node &&
                node != SelectedNode &&
                !SelectedNode.IsAncestorOf(node))
            {
                drgevent.Effect = DragDropEffects.Move;
            }
            else
            {
                drgevent.Effect = DragDropEffects.None;
            }
        }

        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            base.OnDragDrop(drgevent);

            Point point = this.ClientPointToContent(PointToClient(new Point(drgevent.X, drgevent.Y)));

            WbsNode node = WbsLayoutEngine.FindNode(Root, point);
            if (null != node
                && node != SelectedNode)
            {
                if ((drgevent.KeyState & 8) == 8)
                {
                    if (CheckReadOnly())
                        return;
                    Copy();
                    SelectedNode = node;
                    Paste();
                }
                else
                {
                    if (null != SelectedNode &&
                        !SelectedNode.IsRoot &&
                        null != SelectedNode.Parent)
                    {
                        if (CheckReadOnly())
                            return;
                        SelectedNode.Parent.RemoveChild(SelectedNode);
                        node.AddChild(SelectedNode);
                        LayoutTreeAndRefresh();
                        SetDirty();
                    }
                }
            }
        }

        private static bool IsArrowKey(Keys keyData)
        {
            return (keyData == Keys.Left
                || keyData == Keys.Right
                || keyData == Keys.Down
                || keyData == Keys.Up);
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (IsArrowKey(keyData) && !AnyEditMode)
            {
                if (keyData == Keys.Left)
                    NavigateLeft();
                else if (keyData == Keys.Right)
                    NavigateRight();
                else if (keyData == Keys.Up)
                    NavigateUp();
                else if (keyData == Keys.Down)
                    NavigateDown();
                return true;
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (CheckEditModes())
            {
                return;
            }

            // First, actions which don't require a selected node
            bool explicitlyHandled = false;
            switch (e.KeyCode)
            {
                case Keys.F:
                    if (e.Modifiers == Keys.Control)
                        FireRequestFindUI();
                    break;

                case Keys.D0:
                    if (e.Modifiers == Keys.Control)
                        this.Zoom = 1;
                    break;

                case Keys.Home:
                case Keys.Down:
                case Keys.Up:
                case Keys.Right:
                case Keys.Left:
                    if (SelectedNode == null)
                    {
                        NavigateRoot();
                        explicitlyHandled = true;
                    }
                    break;

                case Keys.Enter:
                    if (SelectedNode == null)
                    {
                        EnterPropertiesEditMode();
                        explicitlyHandled = true;
                    }
                    break;
            }

            if (explicitlyHandled)
                return;
            if (SelectedNode == null)
                return;

            // Now for actions which require a selected node
            switch (e.KeyCode)
            {
                case Keys.Insert:
                    InsertNewNode(e.Modifiers);
                    LayoutTreeAndRefresh();
                    SetDirty();
                    break;

                case Keys.Delete:
                    DeleteSelectedNode();
                    LayoutTreeAndRefresh();
                    SetDirty();
                    break;

                case Keys.Left:
                case Keys.Up:
                    if (e.Modifiers == Keys.Control)
                    {
                        MoveSelecedNodeUpwards();
                        LayoutTreeAndRefresh();
                        SetDirty();
                    }
                    break;

                case Keys.Right:
                case Keys.Down:
                    if (e.Modifiers == Keys.Control)
                    {
                        MoveSelectedNodeDownwards();
                        LayoutTreeAndRefresh();
                        SetDirty();
                    }
                    break;

                case Keys.Enter:
                    EnterQuickEditMode();
                    break;

                case Keys.Home:
                    if (e.Modifiers == Keys.Control)
                        NavigateHome();
                    else
                        NavigateTop();
                    break;

                case Keys.End:
                    if (e.Modifiers == Keys.Control)
                        NavigateBottomRight();
                    else
                        NavigateBottom();
                    break;

                case Keys.O:
                    if (e.Modifiers == Keys.Control)
                        OpenWorkItem(SelectedNode);
                    break;

                case Keys.Space:
                    CollapseExpandSelectedNode();
                    break;
            }
        }
        #endregion

        #region Tree Manipulation
        private void CollapseExpandSelectedNode()
        {
            if (SelectedNode != null)
            {
                if (SelectedNode.IsCollapsible)
                    SelectedNode.IsCollapsed = !SelectedNode.IsCollapsed;
                LayoutTreeAndRefresh();
            }
        }

        public void ExpandTree()
        {
            ExpandNode(Root);
            LayoutTreeAndRefresh();
        }

        private void ExpandNode(WbsNode node)
        {
            node.IsCollapsed = false;
            foreach (WbsNode child in node.ChildNodes)
            {
                ExpandNode(child);
            }
        }

        public void CollapseFromLevel(int level)
        {
            CollapseNodeFromLevel(Root, 1, level);
            LayoutTreeAndRefresh();
        }

        private void CollapseNodeFromLevel(WbsNode node, int level, int maxLevel)
        {
            if (level < maxLevel)
            {
                node.IsCollapsed = false;
            }
            if (level == maxLevel)
            {
                if (node.IsCollapsible)
                {
                    node.IsCollapsed = true;
                }
            }
            else
            {
                foreach (WbsNode child in node.ChildNodes)
                {
                    CollapseNodeFromLevel(child, level + 1, maxLevel);
                }
            }
        }

        private void EnterQuickEditMode()
        {
            if (CheckReadOnly())
                return;
            this.EnsureSelectedNodeIsVisibleAndRefresh();

            if (null == SelectedNode)
            {
                EnterPropertiesEditMode();
                return;
            }

            this.FireEditModeEntered();

            Point position = this.VirtualToClient(SelectedNode.LayoutInfo.Position.Add(3, 3));
            if (position.X + QuickEdit.Size.Width > Width)
                position.X = Width - QuickEdit.Size.Width - 3;
            if (position.Y + QuickEdit.Size.Height > Height)
                position.Y = Height - QuickEdit.Size.Height - 3;
            QuickEdit.Location = position;
            QuickEdit.WbsNode = SelectedNode;
            SelectedNode.Wbs = WorkBreakdownStructure;
            QuickEdit.RemainingWork.Text = SelectedNode.RemainingWorkInDisplayUnits.ToString();
            QuickEdit.CompletedWork.Text = SelectedNode.CompletedWorkInDisplayUnits.ToString();
            QuickEdit.TfsClient = TfsClient;
            QuickEdit.DisplayControl();
            _quickEditMode = true;
        }

        private void QuitQuickEditMode()
        {
            _quickEditMode = false;
            QuickEdit.Visible = false;
            
            this.FireEditModeExited();

            if (null != SelectedNode)
            {
                bool hasOnlyChangedTitle =
                    (SelectedNode.IsPublished &&
                    !QuickEdit.HasRefreshedNode
                    && SelectedNode.Title != QuickEdit.Title.Text);
                bool abortEditing = false;
                if (hasOnlyChangedTitle)
                {
                    string oldTitle = SelectedNode.Title;
                    TfsClient.RefreshWbsNode(WorkBreakdownStructure, SelectedNode);
                    if (oldTitle != SelectedNode.Title)
                    {
                        MessageBox.Show(this, "The title in the work item has changed since the last refresh. Editing operation will be aborted.", 
                            "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        abortEditing = true;
                    }
                    QuickEdit.HasRefreshedNode = true;
                }

                if (!abortEditing)
                {
                    SelectedNode.WorkItemType = QuickEdit.WorkItemType.Text;
                    SelectedNode.Title = QuickEdit.Title.Text;
                    SelectedNode.AssignedTo = QuickEdit.AssignedTo.Text;
                    SelectedNode.Wbs = WorkBreakdownStructure;
                    SelectedNode.Iteration = QuickEdit.Iteration.Text;
                    float remainingWork = SelectedNode.RemainingWorkInDisplayUnits;
                    float completedWork = SelectedNode.CompletedWorkInDisplayUnits;
                    string desc = QuickEdit.Description.Text;
                    if (!SelectedNode.IsRoot)
                        SelectedNode.Description = desc;

                    if (float.TryParse(QuickEdit.RemainingWork.Text, out remainingWork))
                        SelectedNode.RemainingWorkInDisplayUnits = remainingWork;
                    else if (String.IsNullOrWhiteSpace(QuickEdit.RemainingWork.Text))
                        SelectedNode.RemainingWorkInDisplayUnits = 0;
                    if (float.TryParse(QuickEdit.CompletedWork.Text, out completedWork))
                        SelectedNode.CompletedWorkInDisplayUnits = completedWork;
                    else if (String.IsNullOrWhiteSpace(QuickEdit.CompletedWork.Text))
                        SelectedNode.CompletedWorkInDisplayUnits = 0;
                    if (SelectedNode.IsPublished &&
                        QuickEdit.HasRefreshedNode)
                    {
                        TfsClient.PublishAllFields(WorkBreakdownStructure, SelectedNode);
                    }
                    SetDirty();
                }

                UpdateTotalWorkAndPlausis();
            }

            this.Invalidate();
            Focus();
        }

        private void EnterPropertiesEditMode()
        {
            this.EnsureSelectedNodeIsVisibleAndRefresh();

            if (CheckReadOnly())
                return;
            int x = (Size.Width - PropertiesPanel.Size.Width) / 2;
            int y = (Size.Height - PropertiesPanel.Size.Height) / 2;
            PropertiesPanel.Location = new Point(x, y);
            PropertiesPanel.Title.Text = WorkBreakdownStructure.Title;
            PropertiesPanel.TID.Text = WorkBreakdownStructure.TID;
            PropertiesPanel.Owner.Text = WorkBreakdownStructure.Owner;
            PropertiesPanel.LinkToConcept.Text = WorkBreakdownStructure.ConceptLink;
            PropertiesPanel.DisplayUnit = WorkBreakdownStructure.DisplayUnit;
            PropertiesPanel.PersistUnit = WorkBreakdownStructure.PersistUnit;
            PropertiesPanel.DaysToHoursFactor = WorkBreakdownStructure.DaysToHoursFactor;
            PropertiesPanel.TfsClient = TfsClient;
            PropertiesPanel.DisplayControl();
            _propertiesEditMode = true;
        }

        private void QuitPropertiesEditMode()
        {
            _propertiesEditMode = false;
            PropertiesPanel.Visible = false;
            WorkBreakdownStructure.Title = PropertiesPanel.Title.Text;
            WorkBreakdownStructure.TID = PropertiesPanel.TID.Text;
            WorkBreakdownStructure.Owner = PropertiesPanel.Owner.Text;
            WorkBreakdownStructure.ConceptLink = PropertiesPanel.LinkToConcept.Text;
            WorkBreakdownStructure.DisplayUnit = PropertiesPanel.DisplayUnit;
            WorkBreakdownStructure.PersistUnit = PropertiesPanel.PersistUnit;
            WorkBreakdownStructure.DaysToHoursFactor = PropertiesPanel.DaysToHoursFactor;
            Focus();
        }

        private void CancelQuickEditMode()
        {
            _quickEditMode = false;
            QuickEdit.Visible = false;
            Focus();
        }

        private void EnterBatchEditMode()
        {
            if (null == SelectedNode)
                return;
            if (CheckReadOnly())
                return;

            int x = SelectedNode.LayoutInfo.Position.X + 3;
            int y = SelectedNode.LayoutInfo.Position.Y + 3;
            if (x + BatchEdit.Size.Width > Width)
                x = Width - BatchEdit.Size.Width - 3;
            if (y + BatchEdit.Size.Height > Height)
                y = Height - BatchEdit.Size.Height - 3;
            BatchEdit.Location = new Point(x, y);
            BatchEdit.WbsNode = SelectedNode;
            BatchEdit.Wbs = WorkBreakdownStructure;
            BatchEdit.NodeFactory = this;
            BatchEdit.DisplayControl();

            _batchEditMode = true;
        }

        private void QuitBatchEditMode(bool save)
        {
            _batchEditMode = false;

            if (save)
            {
                BatchEdit.WriteBackChanges();
                UpdateTotalWorkAndPlausis();
                LayoutTreeAndRefresh();
            }

            Focus();
        }

        private bool QueryQuitBatchEditMode()
        {
            DialogResult res = MessageBox.Show("Do you want to save the changes made in batch edit mode?", "Confirm",
                MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
            if (DialogResult.Yes == res)
            {
                BatchEdit.Visible = false;
                QuitBatchEditMode(true);
                return true;
            }
            else if (DialogResult.No == res)
            {
                BatchEdit.Visible = false;
                QuitBatchEditMode(false);
                return true;
            }
            return false;
        }

        private bool CheckEditModes()
        {
            if (_quickEditMode)
            {
                QuitQuickEditMode();
                return true;
            }
            if (_propertiesEditMode)
            {
                QuitPropertiesEditMode();
                return true;
            }
            if (_batchEditMode)
            {
                QueryQuitBatchEditMode();
                return true;
            }
            return false;
        }

        private void MoveSelectedNodeDownwards()
        {
            if (null == SelectedNode)
                return;
            if (null == SelectedNode.Parent)
                return;
            if (CheckReadOnly())
                return;

            WbsNode parent = SelectedNode.Parent;

            int currentIndex = parent.ChildNodes.IndexOf(SelectedNode);
            if (currentIndex >= parent.ChildNodes.Count - 1)
                return;

            parent.ChildNodes.Remove(SelectedNode);
            currentIndex++;
            parent.ChildNodes.Insert(currentIndex, SelectedNode);
        }

        private void MoveSelecedNodeUpwards()
        {
            if (null == SelectedNode)
                return;
            if (null == SelectedNode.Parent)
                return;
            if (CheckReadOnly())
                return;

            WbsNode parent = SelectedNode.Parent;

            int currentIndex = parent.ChildNodes.IndexOf(SelectedNode);
            if (currentIndex <= 0)
                return;

            parent.ChildNodes.Remove(SelectedNode);
            currentIndex--;
            parent.ChildNodes.Insert(currentIndex, SelectedNode);
        }

        private void DeleteSelectedNode()
        {
            if (null == SelectedNode)
                return;
            if (Root == SelectedNode)
            {
                MessageBox.Show("You can't delete the root.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (CheckReadOnly())
                return;
            if (0 != SelectedNode.ChildNodes.Count)
            {
                MessageBox.Show("Selected node has children and thus can't be deleted.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            bool resetNodeValues = false;
            if (SelectedNode.IsPublished)
            {
                if (!TfsClient.IsConnected)
                {
                    MessageBox.Show("You can't delete a published node without being connected to TFS.", "Information",
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }
                if (DialogResult.OK != MessageBox.Show("The selected node has already been published. Really delete it?", "Confirm",
                    MessageBoxButtons.OKCancel, MessageBoxIcon.Question))
                    return;
                string msg = string.Format("Do you want to reset 'Assigned To' and 'Remaining Work' of the corresponding Work Item (#{0})?\nThe Work Item State will be set to 'Closed'.",
                    SelectedNode.WorkItemId);
                DialogResult res = MessageBox.Show(msg, "Confirm", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (DialogResult.Cancel == res)
                    return;
                if (DialogResult.Yes == res)
                    resetNodeValues = true;
            }

            if (resetNodeValues)
            {
                TfsClient.ResetNodeValues(SelectedNode);
            }

            WbsNode node = SelectedNode;
            if (node.Parent.IsRoot)
                NavigateLeft();
            else
                NavigateUp();
            node.Parent.RemoveChild(node);
        }

        public void EliminateSelectedNode()
        {
            WbsNode node = SelectedNode;
            if (!IsEliminateNodeAllowed(node))
                return;
            int index = node.Parent.ChildNodes.IndexOf(node);
            if (index < 0)
                return;
            List<WbsNode> childList = node.ChildNodes;
            if (childList.Count <= 0)
                return;
            node.Parent.RemoveChild(node);
            for (int i = childList.Count - 1; i >= 0; --i)
            {
                node.Parent.AddChild(childList[i], index);
            }
            SelectedNode = childList[0];
            LayoutTreeAndRefresh();
            SetDirty();
        }

        private void InsertNewNode(Keys keys)
        {
            if (CheckReadOnly())
                return;
            bool insertNewParent = false;
            if (keys != Keys.Alt &&
                (SelectedNode.IsLeaf && !SelectedNode.IsRoot &&
                (SelectedNode.RemainingWorkInDays > 0
                 || SelectedNode.CompletedWorkInDays > 0
                 || !string.IsNullOrEmpty(SelectedNode.AssignedTo))))
            {
                DialogResult res =
                    MessageBox.Show("The selected node has work and/or a person assigned to it.\n\nDo you want to create a new parent node and insert this node underneath instead?",
                        "Confirm", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (res == DialogResult.Cancel)
                    return;
                if (res == DialogResult.Yes)
                    insertNewParent = true;
            }
            if (!insertNewParent)
            {
                if (keys == Keys.Control)
                {
                    EnterBatchEditMode();
                    return;
                }
                WbsNode node = CreateWbsNode();
                if (!SelectedNode.IsRoot &&
                    keys == Keys.Alt)
                {
                    WbsNode parent = SelectedNode.Parent;
                    parent.AddChild(node);
                    SelectedNode = node;
                }
                else
                {
                    SelectedNode.AddChild(node);
                    if (keys != Keys.Shift)
                        SelectedNode = node;
                }
                node.LayoutMode = WbsEditor.WbsNode.NodeLayout.Vertical;
            }
            else
            {
                WbsNode newNode = CreateWbsNode();
                WbsNode selectedNode = SelectedNode;
                WbsNode parent = selectedNode.Parent;
                int insertPosition = parent.ChildNodes.IndexOf(SelectedNode);
                parent.RemoveChild(selectedNode);
                parent.ChildNodes.Insert(insertPosition, newNode);
                newNode.Parent = parent;
                newNode.AddChild(selectedNode);
                SelectedNode = newNode;
                newNode.LayoutMode = WbsEditor.WbsNode.NodeLayout.Vertical;
            }
        }

        // IWbsNodeFactory
        public WbsNode CreateWbsNode()
        {
            WbsNode node = new WbsNode(CurrentWorkItemType);
            //node.Iteration = TfsClient.Iteration;
            node.Iteration = Root.Iteration;
            return node;
        }

        public void SetLayout(WbsSettings.LayoutSize newLayout)
        {
            if (Settings.Layout != newLayout)
            {
                Settings.Layout = newLayout;
                LayoutTreeAndRefresh();
                if (null != LayoutChanged)
                    LayoutChanged(this, new EventArgs());
            }
        }

        private bool CheckReadOnly()
        {
            if (ReadOnly)
            {
                MessageBox.Show("Operation not allowed: WBS is read only.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return true;
            }
            return false;
        }
        #endregion

        #region Layout

        private void EnsureSelectedNodeIsVisibleAndRefresh()
        {
            this.Invalidate();

            WbsNode node = SelectedNode;
            if (node == null)
                return;
            Rectangle bounds = new Rectangle(node.LayoutInfo.Position, node.LayoutInfo.Size);
            this.EnsureVisible(bounds);
        }

        private void LayoutTreeAndRefresh()
        {
            if (ReadOnly)
                Settings.UseWbsCodes = false;

            this.Remeasure();

            UpdateTotalWorkAndPlausis();
            EnsureSelectedNodeIsVisibleAndRefresh();

            if (null != AssigneeListChanged)
                AssigneeListChanged(this, new EventArgs());
        }

        protected override Size Measure(Size available)
        {
            if (this.WorkBreakdownStructure == null)
                return new Size(0, 0);

            return WbsLayoutEngine.LayoutWbs(WorkBreakdownStructure, _settings, TfsClient);
        }

        private void UpdateTotalWorkAndPlausis()
        {
            WorkBreakdownStructure.RecalculateTotalWork();
            WbsPlausiChecker.CheckWbs(WorkBreakdownStructure, TfsClient);
        }

        #endregion

        #region Navigation
        private void NavigateLeft()
        {
            if (SelectedNode == null)
            {
                SelectedNode = Root;
                EnsureSelectedNodeIsVisibleAndRefresh();
                return;
            }
            // Fix the easy case first: Horizontal layout
            if (SelectedNode.Parent != null)
            {
                if (SelectedNode.Parent.LayoutMode == WbsNode.NodeLayout.Horizontal)
                {
                    int indexInParent = SelectedNode.Parent.ChildNodes.IndexOf(SelectedNode);
                    if (indexInParent > 0)
                    {
                        SelectedNode = SelectedNode.Parent.ChildNodes[indexInParent - 1];
                        EnsureSelectedNodeIsVisibleAndRefresh();
                        return;
                    }
                }
            }

            // Now we go heuristic
            Point location = GetNodeLocation(SelectedNode);
            Point targetLocation =
                new Point(location.X - SelectedNode.LayoutInfo.Size.Width - Settings.HorizontalPadding,
                          location.Y);
            NavigateToTargetLocation(targetLocation);
        }

        private void NavigateRight()
        {
            if (SelectedNode == null)
            {
                SelectedNode = Root;
                EnsureSelectedNodeIsVisibleAndRefresh();
                return;
            }
            // Fix the easy case first: Horizontal layout
            if (SelectedNode.Parent != null)
            {
                if (SelectedNode.Parent.LayoutMode == WbsNode.NodeLayout.Horizontal)
                {
                    int indexInParent = SelectedNode.Parent.ChildNodes.IndexOf(SelectedNode);
                    if (indexInParent < SelectedNode.Parent.ChildNodes.Count - 1)
                    {
                        SelectedNode = SelectedNode.Parent.ChildNodes[indexInParent + 1];
                        EnsureSelectedNodeIsVisibleAndRefresh();
                        return;
                    }
                }
            }

            // Now we go heuristic
            Point location = GetNodeLocation(SelectedNode);
            Point targetLocation =
                new Point(location.X + SelectedNode.LayoutInfo.Size.Width + Settings.HorizontalPadding,
                          location.Y);
            NavigateToTargetLocation(targetLocation);
        }

        private void NavigateUp()
        {
            if (SelectedNode == null)
            {
                SelectedNode = Root;
                EnsureSelectedNodeIsVisibleAndRefresh();
                return;
            }

            // Now we go heuristic
            Point location = GetNodeLocation(SelectedNode);
            Point targetLocation =
                new Point(location.X,
                          location.Y - SelectedNode.LayoutInfo.Size.Height - Settings.VerticalPadding);
            NavigateToTargetLocation(targetLocation);
        }

        private void NavigateDown()
        {
            if (SelectedNode == null)
            {
                SelectedNode = Root;
                EnsureSelectedNodeIsVisibleAndRefresh();
                return;
            }

            // Fix the easy case first: Vertical layout
            if (SelectedNode.Parent != null)
            {
                if (SelectedNode.Parent.LayoutMode == WbsNode.NodeLayout.Vertical
                    && !SelectedNode.HasChildNodes)
                {
                    int indexInParent = SelectedNode.Parent.ChildNodes.IndexOf(SelectedNode);
                    if (indexInParent < SelectedNode.Parent.ChildNodes.Count - 1)
                    {
                        SelectedNode = SelectedNode.Parent.ChildNodes[indexInParent + 1];
                        EnsureSelectedNodeIsVisibleAndRefresh();
                        return;
                    }
                }
            }

            // Now we go heuristic
            Point location = GetNodeLocation(SelectedNode);
            Point targetLocation =
                new Point(location.X,
                          location.Y + SelectedNode.LayoutInfo.Size.Height + Settings.VerticalPadding);
            NavigateToTargetLocation(targetLocation);
        }

        private void NavigateToTargetLocation(Point targetLocation)
        {
            // Check for direct matches
            WbsNode targetNode = WbsTraverser.FindNodeWithPredicate(Root,
                delegate(WbsNode node)
                {
                    if (node.LayoutInfo.BoundingBox.Contains(targetLocation)
                        && !WbsLayoutEngine.HasCollapsedAncestor(node))
                        return true;
                    return false;
                });
            if (null != targetNode)
            {
                SelectedNode = targetNode;
                EnsureSelectedNodeIsVisibleAndRefresh();
                return;
            }

            // No direct match, use heuristic
            Dictionary<WbsNode, int> score = new Dictionary<WbsNode, int>();
            WbsTraverser.TraverseTree(Root,
                delegate(WbsNode node)
                {
                    if (node != SelectedNode)
                        score[node] = ScoreDistance(node, targetLocation, false);
                });
            WbsNode minNode = null;
            int minValue = int.MaxValue;
            foreach (WbsNode dictNode in score.Keys)
            {
                int dictScore = score[dictNode];
                if (dictScore < minValue)
                {
                    minNode = dictNode;
                    minValue = dictScore;
                }
            }

            if (null != minNode)
            {
                SelectedNode = minNode;
                EnsureSelectedNodeIsVisibleAndRefresh();
            }
        }

        private static int ScoreDistance(WbsNode node, Point location, bool preferVertical)
        {
            if (WbsLayoutEngine.HasCollapsedAncestor(node))
                return int.MaxValue;
            Point nodeLocation = GetNodeLocation(node);
            int score = Math.Abs(nodeLocation.X - location.X) * (preferVertical ? 3 : 1)
                + Math.Abs(nodeLocation.Y - location.Y) * (preferVertical ? 1 : 3);
            return score;
        }

        private static Point GetNodeLocation(WbsNode node)
        {
            return new Point(node.LayoutInfo.Position.X + node.LayoutInfo.Size.Width / 2,
                            node.LayoutInfo.Position.Y + node.LayoutInfo.Size.Height / 2);
        }

        private WbsNode FindBaseNode(WbsNode node)
        {
            if (node.Parent == null)
                return null;
            if (node.Parent == Root)
                return node;
            return FindBaseNode(node.Parent);
        }

        private void NavigateTop()
        {
            if (null == SelectedNode)
            {
                NavigateHome();
                return;
            }
            if (SelectedNode.IsRoot)
                return;
            if (SelectedNode.Parent == null)
                return;
            if (SelectedNode.Parent.IsRoot)
            {
                NavigateHome();
                return;
            }
            SelectedNode = FindBaseNode(SelectedNode);
            EnsureSelectedNodeIsVisibleAndRefresh();
        }

        private void NavigateHome()
        {
            if (Root.HasChildNodes)
            {
                SelectedNode = Root.ChildNodes[0];
            }
            else
            {
                SelectedNode = Root;
            }
            EnsureSelectedNodeIsVisibleAndRefresh();
        }

        private void NavigateRoot()
        {
            SelectedNode = Root;
            EnsureSelectedNodeIsVisibleAndRefresh();
        }

        private void NavigateBottom()
        {
            if (null == SelectedNode)
                return;
            WbsNode node = SelectedNode;
            if (node.IsRoot)
            {
                if (Root.HasChildNodes)
                    node = Root.ChildNodes[0];
            }
            WbsNode baseNode = FindBaseNode(node);
            if (null == baseNode)
                return;
            WbsNode lastNode = FindLastNode(baseNode);
            if (null == lastNode)
                return;
            SelectedNode = lastNode;
            EnsureSelectedNodeIsVisibleAndRefresh();
        }

        private void NavigateBottomRight()
        {
            WbsNode lastNode = Root;
            if (Root.HasChildNodes)
            {
                lastNode = Root.ChildNodes[Root.ChildNodes.Count - 1];
                lastNode = FindLastNode(lastNode);
                if (null == lastNode)
                    return;
            }
            SelectedNode = lastNode;
            EnsureSelectedNodeIsVisibleAndRefresh();
        }

        private WbsNode FindLastNode(WbsNode node)
        {
            if (null == node)
                return null;
            if (!node.HasChildNodes)
                return node;
            return FindLastNode(node.ChildNodes[node.ChildNodes.Count - 1]);
        }

        private WbsNode FindLastNodeWithRoot(WbsNode root)
        {
            if (root.ChildNodes.Count == 0)
                return root;
            return FindLastNodeWithRoot(root.ChildNodes[root.ChildNodes.Count - 1]);
        }

        public bool SelectFirstHighlightedNode()
        {
            if (Settings.FirstMatchingNode == null)
                return false;

            SelectedNode = Settings.FirstMatchingNode;
            return true;
        }
        #endregion

        #region Loading and saving
        public void New()
        {
            WorkBreakdownStructure = new WorkBreakdownStructure();
            LayoutTreeAndRefresh();
            SetClean();
            ReadOnly = false;
        }

        public bool SaveXml(string xmlFileName)
        {
            if (CheckReadOnly())
                return false;
            if (CheckWbsProperties())
                return false;
            UpdateTotalWorkAndPlausis();
            bool ret = WbsSerializer.Serialize(WorkBreakdownStructure, xmlFileName);
            if (ret)
                SetClean();
            return ret;
        }

        public bool SaveRootToWorkItem()
        {
            if (CheckReadOnly())
                return false;
            if (CheckWbsProperties())
                return false;
            UpdateTotalWorkAndPlausis();
            bool ret = PublishOrSaveToRoot(false);
            if (ret)
                SetClean();
            return ret;
        }

        public bool LoadXml(string xmlFileName)
        {
            WorkBreakdownStructure wbs = WbsSerializer.Deserialize(xmlFileName);
            if (null != wbs)
            {
                WorkBreakdownStructure = wbs;
                LayoutTreeAndRefresh();
                SetClean();
                ReadOnly = false;
                return true;
            }
            return false;
        }

        public bool SetWorkBreakdownStructure(WorkBreakdownStructure wbs)
        {
            if (null == wbs)
                return false;
            WorkBreakdownStructure = wbs;
            LayoutTreeAndRefresh();
            SetClean();
            return true;
        }

        public bool LoadFromWorkItem(int workItemId)
        {
            if (!CheckTfsConnectState())
                return false;
            int rootId = 0;
            if (workItemId <= 0)
            {
                EnterWorkItemIdForm rootIdForm = new EnterWorkItemIdForm();
                rootIdForm.LabelText = "Enter the ID of the Work Breakdown Structure's root work item:";
                DialogResult res = rootIdForm.ShowDialog();
                if (res == DialogResult.OK)
                {
                    rootId = rootIdForm.WorkItemId;
                }
            }
            else
            {
                rootId = workItemId;
            }
            if (rootId > 0)
            {
     			Cursor current = Cursor.Current;
                try
                {
                    Cursor.Current = Cursors.WaitCursor;
                    string xml = TfsClient.GetWorkItemDescription(rootId);

                    if (null == xml)
                        return false;
                
                    WorkBreakdownStructure wbs = WbsSerializer.DeserializeFromString(xml);
                    if (null == wbs)
                        return false;
                    WorkBreakdownStructure = wbs;
                    // While Load, save unmodified XML String
                    WorkBreakdownStructure.XmlOriginal = WbsSerializer.SerializeToString(wbs, TfsClient.IsDescriptionHtml());
                    LayoutTreeAndRefresh();
                    SetClean();
                }
                finally
                {
                    Cursor.Current = current;
                }
                bool refreshWbs = false;
                if (Settings.RefreshAtLoad == WbsSettings.RefreshAtLoadEnum.Ask)
                {
                    if (DialogResult.Yes == MessageBox.Show("Do you want to refresh the WBS' work items now?\n\n(You can change the default behaviour in the preferences dialog)", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1))
                    {
                        refreshWbs = true;
                    }
                }
                else if (Settings.RefreshAtLoad == WbsSettings.RefreshAtLoadEnum.Yes)
                {
                    refreshWbs = true;
                }
                if (refreshWbs)
                {
                    RefreshAll();
                }
                ReadOnly = false;
                return true;
            }
            return false;
        }

        public bool GeneratePersonalizedWbs()
        {
            if (!CheckTfsConnectState())
                return false;
            WorkBreakdownStructure backup = WorkBreakdownStructure;
            try
            {
                WbsGenerator generator = new WbsGenerator(TfsClient);
                WbsGeneratorSettings settings = new WbsGeneratorSettings(TfsClient);
                settings.GenerationMode = GenerationMode.PersonalizedWbs;
                if (null != SelectedNode)
                {
                    if (!string.IsNullOrEmpty(SelectedNode.AssignedTo))
                    {
                        settings.UserName = SelectedNode.AssignedTo;
                    }
                }
                DialogResult res = settings.ShowDialog(this);
                if (DialogResult.OK == res)
                {
                    WorkBreakdownStructure wbs =
                        generator.GenerateWbs(settings.UserName,
                            settings.IncludeOrphanTasks,
                            settings.IncludeClosedWorkItems,
                            settings.IncludeBugs,
                            settings.RefreshWbsNodes,
                            settings.SelectedIterations);
                    WorkBreakdownStructure = wbs;
                    ReadOnly = true;
                    LayoutTreeAndRefresh();
                    SetClean();
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                MessageBox.Show("While creating a personalized WBS, the following error occurred:\n\n" + ex.Message,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            WorkBreakdownStructure = backup;
            LayoutTreeAndRefresh();
            return false;
        }

        public void GenerateWorkloadChart()
        {
            if (!CheckTfsConnectState())
                return;
            try
            {
                WbsGenerator generator = new WbsGenerator(TfsClient);
                WbsGeneratorSettings settings = new WbsGeneratorSettings(TfsClient);
                settings.GenerationMode = GenerationMode.WorkloadAnalysis;
                DialogResult res = settings.ShowDialog();
                if (DialogResult.OK == res)
                {
                    string[] iterations = settings.SelectedIterations;
                    WorkPackageInfo[] conceptInfos = generator.GenerateWorkPackageInfo(
                        settings.IncludeOrphanTasks,
                        settings.IncludeBugs,
                        settings.RefreshWbsNodes,
                        iterations);
                    IterationWorkloadChartDialog dlg = new IterationWorkloadChartDialog(conceptInfos, this, generator, iterations);
                    dlg.ShowDialog(this);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Unexpected error during the creation of a workload chart:\n\n" + ex.Message,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void EditWbsProperties()
        {
            CheckEditModes();
            EnterPropertiesEditMode();
        }

        public void RefreshPlausis()
        {
            UpdateTotalWorkAndPlausis();
            EnsureSelectedNodeIsVisibleAndRefresh();
        }

        private bool CheckWbsProperties()
        {
            if (string.IsNullOrEmpty(WorkBreakdownStructure.TID)
                || string.IsNullOrEmpty(WorkBreakdownStructure.Title)
                || WorkBreakdownStructure.Title.Equals("(untitled)", StringComparison.CurrentCultureIgnoreCase))
            {
                string msg = string.Format("Please verify the TID and Title of the Work Breakdown Structure:\n\n" +
                    "Title: {0}\nTID: {1}\n\nContinue operation?", WorkBreakdownStructure.Title, WorkBreakdownStructure.TID);
                DialogResult res = MessageBox.Show(msg, "Confirm", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

                if (res != DialogResult.Yes)
                    return true;
            }

            return false;
        }
        #endregion

        #region Editing methods
        public void Cut()
        {
            if (CheckReadOnly())
                return;
            if (SelectedNode == null)
                return;
            if (SelectedNode.IsRoot)
            {
                MessageBox.Show("You can't cut the root node.", "Information",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            if (WbsTraverser.ExistsPublishedNodeInTree(SelectedNode))
            {
                DialogResult res = MessageBox.Show("The selected node and/or nodes in the sub tree is/are published.\n\nAre you sure you want to cut out this node?",
                    "Confirm", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (DialogResult.Yes != res)
                    return;
            }

            WbsNode parent = SelectedNode.Parent;
            if (!WbsExporter.CopyNodeToClipboard(SelectedNode))
                return;

            if (null == parent)
                return;

            parent.RemoveChild(SelectedNode);
            SelectedNode = parent;
            LayoutTreeAndRefresh();
            SetDirty();
        }

        public void Copy()
        {
            if (null == SelectedNode)
                return;

            WbsExporter.CopyNodeToClipboard(SelectedNode);
        }

        public void Paste()
        {
            if (CheckReadOnly())
                return;
            if (null == SelectedNode)
                return;

            WbsNode node = WbsExporter.GetNodeFromClipboard(this._wbs);

            List<int> workItemIdList = new List<int>();
            WbsTraverser.GetWorkItemIdsInTree(Root, workItemIdList);

            if (WbsTraverser.ExistsNodeWithWorkItemIdsInTree(node, workItemIdList))
            {
                DialogResult res = MessageBox.Show(
                    "The sub tree which will be pasted into the WBS contains nodes with Work Item IDs\n" +
                    "that are already present in the current tree.\n\n" +
                    "Press 'OK' to continue, these nodes will be imported as non-published nodes.",
                    "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
                if (res == DialogResult.Cancel)
                    return;

                WbsTraverser.ResetWorkItemIdsForList(node, workItemIdList);
            }

            SelectedNode.AddChild(node);
            SetDirty();
            LayoutTreeAndRefresh();
        }

        public void PasteAssignee()
        {
            if (null == SelectedNode)
                return;

            WbsNode node = WbsExporter.GetNodeFromClipboard(this._wbs);

            if (null == node)
                return;

            if (!SelectedNode.AssignedTo.Equals(node.AssignedTo))
            {
                SelectedNode.AssignedTo = node.AssignedTo;
                SetDirty();
                LayoutTreeAndRefresh();
            }
        }

        public void PasteEstimation()
        {
            if (null == SelectedNode)
                return;
            if (!SelectedNode.IsLeaf)
                return;

            WbsNode node = WbsExporter.GetNodeFromClipboard(this._wbs);

            if (null == node)
                return;

            if ((Math.Abs(SelectedNode.RemainingWorkInDays - node.RemainingWorkInDays) > 0.001)
                || (Math.Abs(SelectedNode.CompletedWorkInDays - node.CompletedWorkInDays) > 0.001))
            {
                SelectedNode.RemainingWorkInDays = node.RemainingWorkInDays;
                SelectedNode.CompletedWorkInDays = node.CompletedWorkInDays;
                SetDirty();
                LayoutTreeAndRefresh();
            }
        }

        /// <summary>
        /// Copies the currently set iteration to the selected node.
        /// </summary>
        public void CopyIterationToSelectedNode()
        {
            if (!TfsClient.IsConnected)
                return;
            if (SelectedNode == null)
                return;

            if (SelectedNode.IsPublished)
            {
                TfsClient.RefreshWbsNode(WorkBreakdownStructure, SelectedNode);
            }
            if (SelectedNode.Iteration == TfsClient.Iteration)
                return;

            SelectedNode.Iteration = TfsClient.Iteration;
            if (SelectedNode.IsPublished)
            {
                TfsClient.PublishAllFields(WorkBreakdownStructure, SelectedNode);
            }
            SetDirty();
            LayoutTreeAndRefresh();

        }
        #endregion

        #region Printing and Exporting
        public bool ExportAsImage(string fileName)
        {
            return WbsExporter.ExportWbsAsImage(WorkBreakdownStructure, fileName, null, Settings);
        }

        public bool ExportAsEmf(string fileName)
        {
            using (Graphics g = CreateGraphics())
            {
                return WbsExporter.ExportWbsAsEmf(WorkBreakdownStructure, fileName, null, Settings, g.GetHdc());
            }
        }

        public void Print()
        {
            PrintDialog printDialog = new PrintDialog();
            DialogResult res = printDialog.ShowDialog();
            if (res == DialogResult.OK)
            {
                Print(printDialog.PrinterSettings);
            }
        }

        public void Print(PrinterSettings printerSettings)
        {
            PrintDocument print = new PrintDocument();
            print.PrintPage += new PrintPageEventHandler(PrintCanvas);
            print.PrinterSettings = printerSettings;
            print.Print();
        }

        private void PrintCanvas(Object sender, PrintPageEventArgs ev)
        {
            int pageWidth = ev.MarginBounds.Width;
            int pageHeight = ev.MarginBounds.Height;

            int wbsWidth = Root.LayoutInfo.SubTreeSize.Width;
            int wbsPageWidth = wbsWidth;
            int wbsHeight = Root.LayoutInfo.SubTreeSize.Height;
            int wbsPageHeight = wbsHeight;

            bool rotate = false;
            if (wbsWidth > wbsHeight)
            {
                rotate = true;
                int tmp = wbsPageWidth;
                wbsPageWidth = wbsPageHeight;
                wbsPageHeight = tmp;
            }

            float widthRatio = ((float)pageWidth) / ((float)wbsPageWidth);
            float heightRatio = ((float)pageHeight) / ((float)wbsPageHeight);
            float ratio = Math.Min(widthRatio, heightRatio);

            wbsPageHeight = (int)(((float)wbsPageHeight) * ratio);
            wbsPageWidth = (int)(((float)wbsPageWidth) * ratio);

            bool checkPlausis = Settings.CheckPlausis;
            Settings.CheckPlausis = Settings.PrintPlausis;

            int leftOffset = (ev.PageBounds.Width - wbsPageWidth) / 2;
            int topOffset = (ev.PageBounds.Height - wbsPageHeight) / 2;

            Matrix matrix = new Matrix();
            if (rotate)
            {
                matrix.Rotate(90f, MatrixOrder.Append);
                matrix.Translate(wbsHeight, 0f, MatrixOrder.Append);
            }
            matrix.Multiply(new Matrix(ratio, 0f, 0f, ratio, 0f, 0f), MatrixOrder.Append);
            matrix.Translate(leftOffset, topOffset, MatrixOrder.Append);
            ev.Graphics.Transform = matrix;
            WbsRenderer.RenderWbs(Root, Settings, ev.Graphics, WorkBreakdownStructure, null, TfsClient.Iteration);

            Settings.CheckPlausis = checkPlausis;
        }

        public void ShowWorkloadInformation(WbsNode node)
        {
            if (null == node)
                return;
            WorkloadInfoForm workloadInfoForm = new WorkloadInfoForm();
            workloadInfoForm.WbsNode = node;
            workloadInfoForm.DisplayForm();
        }

        public void CopyEstimationToClipboard()
        {
            EnterWorkItemIdForm form = new EnterWorkItemIdForm();
            form.Text = "Enter percentage";
            form.LabelText = "Enter the percentage for the min and max estimations; 'remaining work' is taken as the typical estimation:";
            form.CheckBoxText = "Also include 'completed work' in the estimation.";
            form.CheckBoxChecked = false;
            form.WorkItemId = _lastEstimationPercentage;
            if (DialogResult.OK == form.ShowDialog())
            {
                // Yes, I know, bad re-use.
                float percentage = form.WorkItemId;
                bool includeCompleted = form.CheckBoxChecked;
                _lastEstimationPercentage = form.WorkItemId;

                WbsExporter.CopyEstimationToClipboard(WorkBreakdownStructure, percentage, includeCompleted);

                MessageBox.Show("The estimation was copied to the clipboard. You can paste it in Excel.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        #endregion

        #region Event Handlers
        private void UpdateContextMenuItems(object sender, WbsEventArgs e)
        {
            // Eliminate node menu item
            bool eliminateNode = IsEliminateNodeAllowed(e.WbsNode);
            eliminateNodeToolStripMenuItem.Enabled = eliminateNode;
        }

        private bool IsEliminateNodeAllowed(WbsNode node)
        {
            bool eliminateNode = true;
            if (null == node)
            {
                eliminateNode = false;
            }
            else
            {
                if (node.IsPublished)
                    eliminateNode = false;
                if (node.IsRoot)
                    eliminateNode = false;
                if (node.ChildNodes.Count <= 0)
                    eliminateNode = false;
                if (null == node.Parent)
                    eliminateNode = false;
            }
            return eliminateNode;
        }

        private void printToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Print();
        }

        private void _normalViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLayout(WbsSettings.LayoutSize.Normal);
        }

        private void _smallViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLayout(WbsSettings.LayoutSize.Small);
        }

        private void largeViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLayout(WbsSettings.LayoutSize.Large);
        }

        private void copyTaskListToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            WbsExporter.CopyTaskListToClipboard(WorkBreakdownStructure);
        }

        private void exportWorkloadPerPersonToolStripMenuItem_Click(object sender, EventArgs e)
        {
            WbsExporter.CopyWorkloadToClipboard(Root);
        }

        private void QuickEdit_OnQuitQuickEdit(object sender, EventArgs e)
        {
            QuitQuickEditMode();
        }

        private void QuickEdit_OnCancelQuickEdit(object sender, EventArgs e)
        {
            CancelQuickEditMode();
        }

        private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EnterPropertiesEditMode();
        }

        private void PropertiesPanel_OnQuitEdit(object sender, EventArgs e)
        {
            QuitPropertiesEditMode();
        }

        private void QuickEdit_OnRequestOpenWorkItem(object sender, WbsEventArgs e)
        {
            OpenWorkItem(e.WbsNode);
        }

        private void QuickEdit_OnRequestPublishWorkItem(object sender, WbsEventArgs e)
        {
            PublishWorkItem(e.WbsNode);
        }

        private void TfsClient_OnSuccessfullyConnected(object sender, EventArgs e)
        {
            if (null != TfsSuccessfullyConnected)
                TfsSuccessfullyConnected(this, new EventArgs());
        }

        private void quickEditToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CheckEditModes();
            if (null != SelectedNode)
                EnterQuickEditMode();
        }

        private void _openWorkItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenWorkItem(SelectedNode);
        }

        private void publishWorkItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PublishWorkItem(SelectedNode);
        }

        private void refreshWorkItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            RefreshWorkItem(SelectedNode);
        }

        private void attachToWorkItemToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AttachToWorkItem(SelectedNode);
        }

        private void insertChildNodeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            InsertNewNode(Keys.None);
            LayoutTreeAndRefresh();
        }

        private void _deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DeleteSelectedNode();
            LayoutTreeAndRefresh();
        }

        private void _batchEditPanel_CancelRequested(object sender, EventArgs e)
        {
            bool save = false;
            QuitBatchEditMode(save);
        }

        private void _batchEditPanel_SaveRequested(object sender, EventArgs e)
        {
            bool save = true;
            QuitBatchEditMode(save);
        }

        private void batchEditToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EnterBatchEditMode();
        }

        private void moveNodeupToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MoveSelecedNodeUpwards();
            LayoutTreeAndRefresh();
            SetDirty();
        }

        private void moveNodeDownToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MoveSelectedNodeDownwards();
            LayoutTreeAndRefresh();
            SetDirty();
        }

        private void showWorkloadInformationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowWorkloadInformation(Root);
        }

        private void showWorkloadInformationToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            ShowWorkloadInformation(SelectedNode);
        }

        private void copyEstimationToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CopyEstimationToClipboard();
        }

        private void collapseExpandNodeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CollapseExpandSelectedNode();
        }

        private void openWBSToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == SelectedNode)
                return;
            LoadFromWorkItem(SelectedNode.WorkItemId);
        }

        private void onWorkBreakdownStructure_TotalWorkChanged(object sender, EventArgs e)
        {
            SetDirty();
        }

        private void eliminateNodeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EliminateSelectedNode();
        }
        #endregion

        #region Team System public Methods
        public void OpenWorkItem(WbsNode node)
        {
            if (null == node)
                return;
            if (!node.IsPublished)
            {
                MessageBox.Show("You can't open a work item that has not been published.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            if (!CheckTfsConnectState())
                return;

            TfsClient.OpenWbsNode(WorkBreakdownStructure, node);
            UpdateTotalWorkAndPlausis();
            EnsureSelectedNodeIsVisibleAndRefresh();
            SetDirty();
        }

        public void PublishWorkItem(WbsNode node)
        {
            if (null == node)
                return;
            if (node.IsPublished)
                return;
            if (node.HasChildNodes && !node.IsRoot)
            {
                MessageBox.Show("Can't publish nodes with children. Not a work item!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (CheckReadOnly())
                return;
            if (!CheckTfsConnectState())
                return;
            if (!node.IsRoot)
            {
                TfsClient.PublishWbsNode(WorkBreakdownStructure, node);
            }
            else
            {
                DialogResult res = MessageBox.Show("This will publish the Work Breakdown Structure definition (the XML) to a work item.\n\n" +
                    "Do you want to proceed?", "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
                if (DialogResult.OK == res)
                    PublishRoot();
            }
            EnsureSelectedNodeIsVisibleAndRefresh();
            SetDirty();
        }

        public void RefreshWorkItem(WbsNode node)
        {
            RefreshWorkItem(node, true, false);
        }

        private void RefreshWorkItem(WbsNode node, bool recalculateWork, bool refreshAll)
        {
            if (null == node)
                return;
            if (!node.IsPublished)
                return;
            if (!CheckTfsConnectState())
                return;

            TfsClient.RefreshWbsNode(WorkBreakdownStructure, node);
            if (recalculateWork)
                UpdateTotalWorkAndPlausis();
            EnsureSelectedNodeIsVisibleAndRefresh();
            if (!refreshAll)
            {
                SetDirty();
            }
        }

        public void PublishAll()
        {
            if (CheckReadOnly())
                return;
            bool publishToRoot = false;
            if (!Root.IsPublished)
            {
                DialogResult res = MessageBox.Show("Do you want to publish the XML to the root node?\n\nThis means you can load the WBS definition directly from Team System.",
                    "Confirm", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (res == DialogResult.Yes)
                    publishToRoot = true;
                else if (res == DialogResult.Cancel)
                    return;
            }
            Cursor currentCursor = Cursor.Current;
            Cursor.Current = Cursors.WaitCursor;
            UpdateTotalWorkAndPlausis();
            int notPublished = 0;
            int count = CountLeafs(Root, ref notPublished);
            if (publishToRoot && !Root.IsLeaf)
            {
                notPublished++;
            }
            if (notPublished > 0)
            {
                string msg = string.Format("This operation will create {0} new Work Items with the following settings:\n\nTeam Project: {1}\nIteration: {2}\n\nThe iteration setting will only apply to those work items not having a specific iteration path set (i.e. '/'), other work items will keep their set iteration path.\n\nDo you want to proceed?",
                    notPublished,
                    TfsClient.TeamProject,
                    TfsClient.Iteration);
                DialogResult res = MessageBox.Show(msg,
                    "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
                if (res != DialogResult.OK)
                    return;
            }
            if (null != LengthyOperationStarted)
                LengthyOperationStarted(this, new WbsLengthyOperationEventArgs(Root, count));
            int current = 0;
            PublishNode(Root, ref current);
            if (publishToRoot)
            {
                PublishRoot();
                // Now link all leafs to the root
                TfsClient.SetWorkItemLinks(WorkBreakdownStructure);
            }
            if (null != LengthyOperationFinished)
                LengthyOperationFinished(this, new WbsLengthyOperationEventArgs(Root, count));
            Cursor.Current = currentCursor;
            LayoutTreeAndRefresh();
            SetDirty();
        }

        public void RePublishTitles()
        {
            if (CheckReadOnly())
                return;
            Cursor current = Cursor.Current;
            Cursor.Current = Cursors.WaitCursor;
            int nodeCount = CountPublishedNodes(Root);
            if (null != LengthyOperationStarted)
                LengthyOperationStarted(this, new WbsLengthyOperationEventArgs(Root, nodeCount));
            int count = 0;
            RePublishTitles(Root, ref count);
            if (null != LengthyOperationFinished)
                LengthyOperationFinished(this, new WbsLengthyOperationEventArgs(Root, nodeCount));
            Cursor.Current = current;
        }

        public void RefreshAll()
        {
            Cursor current = Cursor.Current;
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                int nodeCount = CountPublishedNodes(Root);
                if (null != LengthyOperationStarted)
                    LengthyOperationStarted(this, new WbsLengthyOperationEventArgs(Root, nodeCount));
                int count = 0;
                RefreshNode(Root, ref count, true);
                if (null != LengthyOperationFinished)
                    LengthyOperationFinished(this, new WbsLengthyOperationEventArgs(Root, nodeCount));

                LayoutTreeAndRefresh();
                SetDirty();
            }
            finally
            {
                Cursor.Current = current;
            }
        }

        public void AttachToWorkItem(WbsNode node)
        {
            if (null == node)
                return;
            if (node.IsRoot)
            {
                MessageBox.Show("You can't attach a node to the root. Load WBS from Work Item instead.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (!TfsClient.IsConnected)
            {
                MessageBox.Show("Not connected to Team System.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (CheckReadOnly())
                return;
            if (node.IsPublished)
            {
                DialogResult res = MessageBox.Show("This node has already been published. Do you really want to re-attach it to a different work item?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (res != DialogResult.Yes)
                    return;
            }

            {
                EnterWorkItemIdForm workItemIdForm = new EnterWorkItemIdForm();
                workItemIdForm.LabelText = "Please enter the Id of the Work Item to import into the selected Work Breakdown Structure node:";
                DialogResult res = workItemIdForm.ShowDialog(this);
                if (res == DialogResult.OK)
                {
                    int workItemId = workItemIdForm.WorkItemId;
                    if (0 == workItemId)
                        return;
                    TfsClient.AttachNodeToWorkItem(WorkBreakdownStructure, node, workItemId);

                    UpdateTotalWorkAndPlausis();
                    EnsureSelectedNodeIsVisibleAndRefresh();
                    SetDirty();
                }
            }
        }

        public void DisplayWorkItemGrid()
        {
            if (!TfsClient.IsConnected)
            {
                MessageBox.Show("Not connected to Team System.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (CheckReadOnly())
                return;
            if (DialogResult.Yes !=
                MessageBox.Show("Prior to working with the work items, WBS Editor needs to refresh\n" +
                "all work items/nodes from the TFS data base.\n\n" +
                "Do you want to continue?", "Confirm",
                MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
                return;

            RefreshAll();

            this.EnsureSelectedNodeIsVisibleAndRefresh();
            TfsWorkItemForm wiForm = new TfsWorkItemForm();
            wiForm.Wbs = WorkBreakdownStructure;
            wiForm.TfsClient = TfsClient;
            if (DialogResult.Cancel == wiForm.ShowDialog())
                return;

            LayoutTreeAndRefresh();
            SetDirty();
        }

        public void DisplayIterationDashboard()
        {
            IterationDashboardForm dashForm = new IterationDashboardForm();
            dashForm.DashboardControl.Settings = Settings;
            dashForm.DashboardControl.TfsClient = TfsClient;
            dashForm.DashboardControl.Wbs = WorkBreakdownStructure;

            dashForm.ShowDialog();
        }
        #endregion

        #region Team System private methods
        public bool CheckTfsConnectState()
        {
            if (!TfsClient.IsConnected)
            {
                TfsConnectEventArgs e = new TfsConnectEventArgs();
                if (null != TfsConnectRequested)
                    TfsConnectRequested(this, e);
                if (!e.HasHandledConnect)
                    return false;
            }

            if (!TfsClient.IsConnected)
            {
                MessageBox.Show("Can't perform operation without being connected Team System.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            return true;
        }

        private bool SaveToRoot()
        {
            return PublishOrSaveToRoot(false);
        }

        private bool PublishRoot()
        {
            return PublishOrSaveToRoot(true);
        }

        private bool PublishOrSaveToRoot(bool publish)
        {
            try
            {
                string xml = WbsSerializer.SerializeToString(WorkBreakdownStructure, TfsClient.IsDescriptionHtml());
                if (null == xml)
                    return false;
                Root.Description = xml;
                if (publish)
                {
                    if (TfsClient.PublishWbsNode(WorkBreakdownStructure, Root))
                    {
                        if (null != WbsRootWasPublished)
                            WbsRootWasPublished(this, new EventArgs());
                        return PublishOrSaveToRoot(false);
                    }
                    return false;
                }
                else
                {
                    if (!TfsClient.CheckLastSave(WorkBreakdownStructure))
                    {
                        return false;
                    }
                    DateTime saveTime = new DateTime();
                    bool returnValue = TfsClient.PublishDescription(WorkBreakdownStructure, Root, ref saveTime);
                    if (returnValue)
                    {
                        WorkBreakdownStructure.LastSaveDate = saveTime;
                        // While Save, update the current, saved XML String
                        WorkBreakdownStructure.XmlOriginal = WbsSerializer.SerializeToString(WorkBreakdownStructure, TfsClient.IsDescriptionHtml());
                    }
                    return returnValue;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("An unexpected error occurred during the publishing of the root node:\n\n" + ex.Message, "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return false;
        }

        private void RePublishTitles(WbsNode node, ref int count)
        {
            if (node.IsPublished)
            {
                PublishTitle(node);
                count++;
                if (null != LengthyOperationTick)
                    LengthyOperationTick(this, new WbsLengthyOperationEventArgs(node, count));
            }

            foreach (WbsNode child in node.ChildNodes)
            {
                RePublishTitles(child, ref count);
            }
        }

        private void PublishNode(WbsNode node, ref int count)
        {
            if (node.IsRoot && node.IsPublished)
            {
                PublishTitle(node);
            }

            if (node.IsLeaf)
            {
                if (!node.IsPublished)
                    PublishWorkItem(node);
                else
                    PublishTitle(node);
                count++;
                if (null != LengthyOperationTick)
                    LengthyOperationTick(this, new WbsLengthyOperationEventArgs(node, count));
            }
            else
            {
                foreach (WbsNode child in node.ChildNodes)
                {
                    PublishNode(child, ref count);
                }
            }
        }

        private void PublishTitle(WbsNode node)
        {
            if (!CheckTfsConnectState())
                return;

            TfsClient.PublishTitle(WorkBreakdownStructure, node);
        }

        private int CountLeafs(WbsNode node, ref int notPublished)
        {
            int count = 0;
            if (node.IsLeaf)
            {
                count++;
                if (!node.IsPublished)
                    notPublished++;
            }
            foreach (WbsNode child in node.ChildNodes)
            {
                count += CountLeafs(child, ref notPublished);
            }
            return count;
        }

        private int CountPublishedNodes(WbsNode node)
        {
            int count = 0;
            if (node.IsPublished)
                count++;
            foreach (WbsNode child in node.ChildNodes)
            {
                count += CountPublishedNodes(child);
            }
            return count;
        }

        private void RefreshNode(WbsNode node, ref int currentCount, bool refreshAll)
        {
            if (!node.IsRoot)
            {
                RefreshWorkItem(node, false, refreshAll);
                if (node.IsPublished)
                {
                    currentCount++;
                    if (null != LengthyOperationTick)
                        LengthyOperationTick(this, new WbsLengthyOperationEventArgs(node, currentCount));
                }
            }
            foreach (WbsNode child in node.ChildNodes)
            {
                RefreshNode(child, ref currentCount, refreshAll);
            }
        }

        private bool HasChangeInXml()
        {
            if (Root != null && WorkBreakdownStructure != null)
            {
                if ((Root.IsPublished && TfsClient.IsConnected) || !Root.IsPublished)
                {
                    bool asHtml = Root.IsPublished ? TfsClient.IsDescriptionHtml() : true;
                    string xmlNew = WbsSerializer.SerializeToString(WorkBreakdownStructure, asHtml);
                    bool equal = TfsClient.XmlMatch(xmlNew, WorkBreakdownStructure.XmlOriginal);
                    return !equal;
                }
            }
            return true;
        }
        #endregion

        private void verticalToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == SelectedNode)
                return;
            if (SelectedNode.IsRoot)
                return;
            SelectedNode.LayoutMode = WbsNode.NodeLayout.Vertical;
            LayoutTreeAndRefresh();
        }

        private void horizontalToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == SelectedNode)
                return;
            if (SelectedNode.IsRoot)
                return;
            SelectedNode.LayoutMode = WbsNode.NodeLayout.Horizontal;
            LayoutTreeAndRefresh();
        }

        private void pairedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == SelectedNode)
                return;
            if (SelectedNode.IsRoot)
                return;
            SelectedNode.LayoutMode = WbsNode.NodeLayout.Paired;
            LayoutTreeAndRefresh();
        }
    }
}
